# !/usr/bin/env python
# -*-coding:utf-8 -*-
"""
# Author     ：hu_cl
# Date       ：2022/1/27 16:53
# File       : icmp_2.1.py
# Description：
"""
# !/usr/bin/python
# -*- coding: UTF-8 -*-
import platform
import re
import socket
import struct
import time
import array
import random
import os
import ctypes


def system_get_platform_info():
    sysplatform = platform.system().lower().strip()
    python_bit = platform.architecture()[0]
    python_bit_num = re.findall('(\d+)\w*', python_bit)[0]
    return sysplatform, python_bit_num


sys_platform, python_bit_num = system_get_platform_info()


def load_lib(libpath):
    if python_bit_num == '32':
        if sys_platform == "linux":
            libpath = libpath.replace('\\', '/')
            c_lib = ctypes.cdll.LoadLibrary(libpath)
            code, msg = 200, c_lib
        elif sys_platform == "windows":
            c_lib = ctypes.windll.LoadLibrary(libpath)
            code, msg = 200, c_lib
        else:
            code, msg = 0, None
    else:
        code, msg = 0, None

    return code, msg


class LoadLibIoNet:
    path = os.getcwd()
    pathnow = os.path.abspath('io_net.dll')

    def __init__(self):
        self._p_Command = None
        self._p_creatpcap = None
        self._p_openpcap = None
        self.__hand_len = 64
        self.handle = (ctypes.c_char * self.__hand_len)()
        self.loadlib(LoadLibIoNet.pathnow)

    def loadlib(self, lib_path):
        code, c_lib_handle = load_lib(lib_path)

        # int CreatePcap(const char *devName, void *headle, int length)
        self._p_creatpcap = c_lib_handle.pyCreatePcap
        self._p_creatpcap.argtypes = (ctypes.c_char_p, ctypes.c_void_p, ctypes.c_int)
        self._p_creatpcap.restype = ctypes.c_int

        # int OpenPcap(void *obj);
        self._p_openpcap = c_lib_handle.pyOpenPcap
        self._p_openpcap.argtype = ctypes.c_void_p
        self._p_openpcap.restype = ctypes.c_int

        # command msg
        self._p_Command = c_lib_handle.pyCommand
        self._p_Command.argtypes = (
            ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_int)
        self._p_Command.restype = ctypes.c_int

    @property
    def p_creatpcap(self):
        return self._p_creatpcap

    @property
    def p_openpcap(self):
        return self._p_openpcap

    @property
    def p_command(self):
        return self._p_Command


class IoNet:
    def __init__(self):
        self.libpcappolicy = LoadLibIoNet()
        self.__hand_len = 64
        self.handle = (ctypes.c_char * self.__hand_len)()
        self.create("ts/sms_policy/")
        self.open()

    def create(self, devName):
        # self.handle = bytes(64) # handle必须不小于64字节空间
        result = self.libpcappolicy.p_creatpcap(devName.encode('utf-8'), ctypes.cast(self.handle, ctypes.c_void_p),
                                                self.__hand_len)
        print('creat', result)
        return result

    def open(self):
        # Result = pcap_lib.OpenPcap(self.handle)
        result = self.libpcappolicy.p_openpcap(ctypes.byref(self.handle))
        print('open', result)
        return result

    def command(self, type, inBuffer, inLeng, outLeng):
        """
        :param type:  注册类型
        :param inBuffer: 传入字符串
        :param inLeng: 传入长度
        :param outLeng: 输出长度
        :return: 返回码，返回值
        """
        b_inbuffer = inBuffer
        str_by = ctypes.create_string_buffer(inLeng + 4)
        if type != b"ts/CryptoXorD/":
            str_by.value = b_inbuffer
        else:
            str_by = b_inbuffer

        outBuffer = ctypes.create_string_buffer(outLeng)
        # print(f"Command  type:{type}  inBuffer:{inBuffer}")
        result = self.libpcappolicy.p_command(self.handle, type, str_by, inLeng, outBuffer, outLeng)
        return result, outBuffer


meth_c_lib = IoNet()

# Encrypt, decrypt  加密 解密
def en_de_msg(data):
    code, msg = meth_c_lib.command(b"ts/CryptoXorE/", data, len(data), len(data) + 32)  # 消息加密
    return code, msg.raw


def de_en_msg(data, leng):
    code, msg = meth_c_lib.command(b"ts/CryptoXorD/", data, leng, leng)  # 消息解密
    return code, msg


class IcmpScan:

    def __init__(self, type='send', s_ip='127.0.0.1', s_port=0, d_ip='127.0.0.1', d_port=0, msg='try_icmp', timeout=3):
        self.type = type  # send  reply
        self.s_ip = s_ip
        self.s_port = s_port
        self.d_ip = d_ip
        self.d_port = d_port
        self.msg = msg.encode('utf-8')
        self.__data = None  # 需要发送的数据
        self.__id = random.randint(1000, 65535)
        self.timeout = timeout
        self.socket = self.rawSocket
        self.finished = False

    @property
    def rawSocket(self):
        try:
            Sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
            Sock.settimeout(self.timeout)
        except Exception as e:
            Sock = self.rawSocket
        return Sock

    def inCksum(self, packet):
        if len(packet) & 1:
            packet = packet + b'\0'
        words = array.array('h', packet)
        sum = 0
        for word in words:
            sum += (word & 0xffff)
        sum = (sum >> 16) + (sum & 0xffff)
        sum = sum + (sum >> 16)
        return (~sum) & 0xffff

    def create_packet(self):
        header = struct.pack('bbHHh', 8, 0, 0, self.__id, 0)
        packet = header + self.__data
        chkSum = self.inCksum(packet)
        header = struct.pack('bbHHh', 8, 0, chkSum, self.__id, 0)
        return header + self.__data

    def create_rec_ping(self):
        header = struct.pack('bbHHh', 0, 0, 0, self.__id, 0)
        packet = header + self.__data
        chkSum = self.inCksum(packet)
        header = struct.pack('bbHHh', 0, 0, chkSum, self.__id, 0)
        return header + self.__data

    def start(self):
        # 加密信息发送
        code, data = en_de_msg(self.msg)
        if code < 0:
            print('加密信息错误', self.msg)
        else:
            self.msg = data
            self.__data = struct.pack('d', time.time()) + self.msg


        time_space = 1 / 1000
        # t = threading.Thread(target=self.recv_packet, )
        # t.start()
        try:
            if self.type == 'send':
                packet = self.create_packet()
                self.socket.sendto(packet, (self.d_ip, self.d_port))
                print('ping 已发送', self.d_ip, self.d_port)
            else:
                rec_packet = self.create_rec_ping()
                self.socket.sendto(rec_packet, (self.s_ip, self.s_port))
                print('ping回包 已发送', self.s_ip, self.s_port)

            time.sleep(time_space)
        except socket.timeout:
            print('ping 超时')
        except Exception as e:
            print('ping err', e)
        self.finished = True
        time.sleep(self.timeout + 1)
        self.socket.close()


if __name__ == '__main__':
    # "ts/CryptoXorE/"  加密
    # "ts/CryptoXorD/", 解密  解密大32个字节
    # ip_list = '192.168.2.245'
    # msg = 'hucl_test'
    # print(msg, len(msg))
    # len0, msg0 = en_de_msg(msg)
    # print('en_de_mesg', len0, msg0.raw, )
    # len1, msg1 = de_en_msg(msg0.raw, len0)
    # print('de_en_mesg', len1, msg1.value.decode('utf-8'))
    icmp_scan = IcmpScan(type='reply', s_ip='192.168.2.245', msg='hucl_test')
    icmp_scan.start()
    exit(0)
